#include <sys/select.h>
#include "sock.hpp"

// fd_set是一个位图类型, 它是用位图表示多个fd的, 每个字节可比表示八个bit, 所以最多能表示 n*8个fd
#define FDSET_NUM (sizeof(fd_set) * 8)
#define DEL -1
#define BUFFER_SIZE 1024
#define CLIENT_CLOSE 0
#define RECV_ERROR 1

vector<int> fdArray;
unordered_map<int, string> UserNetworkData;

void Format(const char *str);
void PrintfdArray();
void EstablishMap(int, string &, uint16_t &);
void HandlerEvent(int listensockfd, fd_set &readfds);

//=========================================================================================

int main(int argc, char *argv[])
{
    if (argc < 2)
        Format(argv[0]);
    std::string ip;
    if (argc == 3)
        ip = argv[1];

    int listensockfd = Sock::Socket();
    if (listensockfd < 0)
        exit(4);
    Sock::Bind(listensockfd, ip, std::stoi(argv[2]));
    Sock::Listen(listensockfd);

    fdArray.resize(FDSET_NUM, DEL); // 初始化数组 -- 一共可以保存FDSET_NUM个fd
    fdArray[0] = listensockfd;      // 0下标默认为sockfd

    while (true)
    {
        // 监听socket,本质要先进行三次握手,client给我发送SYN报文 -> 建立连接本质是IO
        // 一个建立好的连接称之为: 读事件就绪, listensockfd也要关心读事件就绪(等待连接请求报文到来)
        // accept: 等(阻塞) + "数据拷贝"(listen监听到连接请求并且完成三次握手, 才会被accept拿到应用层)
        // int serverfd = Sock::Accept(listensockfd, &ClientIp, &ClientPort);

        // 编写多路转接代码, 必须等待事件就绪了, 才能调用IO函数
        int max_fd = DEL;
        fd_set readfds;
        FD_ZERO(&readfds);
        timeval timeout{2, 0};

        for (auto fd : fdArray)
        {
            if (fd == DEL)
                continue;         // 1.过滤不合法的fd
            FD_SET(fd, &readfds); // 2.添加所有合法的fd到readfds中，方便select统一进行就绪监听
            if (max_fd < fd)
                max_fd = fd;     // 3.更新fd最大值
        }

        // 位图集每次输出时会填充覆盖原来的位图集, 每一次调用select，需要对历史的数据和sock进行重新设置
        int n = select(max_fd + 1, &readfds, nullptr, nullptr, &timeout); // 不考虑写和异常事件就绪

        switch (n)
        {
        case 0:
            cout << "time out....... --> (时间戳: " << (unsigned long)time(nullptr) << ")" << endl;
            break;
        case -1:
            cerr << "select error: " << strerror(errno) << endl;
            exit(4);
            break;
        default:
            // 等待成功
            HandlerEvent(listensockfd, readfds);
            break;
        }
    }
    return 0;
}

//=========================================================================================

void Format(const char *str)
{
    std::cout << "Format: " << str << "  [ip]  port" << std::endl;
    exit(3);
}

void PrintfdArray()
{
    cout << "当前合法sock fd: ";
    for (auto fd : fdArray)
    {
        if (fd != DEL)
            cout << fd << " ";
    }
    cout << endl;
}

// 建立serverfd和Client网络基本数据的映射关系
void EstablishMap(int serverfd, string &ClientIp, uint16_t &ClientPort)
{
    string UserData = ClientIp + ":" + std::to_string(ClientPort);
    UserNetworkData[serverfd] = UserData;
}

// 通过标记位判断recv读取时出现什么错误
void recvError_Or_Close(int flags, int &serverfd, string &UserData)
{
    if (flags == CLIENT_CLOSE)
        cout << "Client[" << UserData << "] quit, server close, fd: " << serverfd << endl;
    else if (flags == RECV_ERROR)
        cout << "Client[" << UserData << "] quit, Server recv error, fd: " << serverfd << endl;

    close(serverfd);
    serverfd = -1; // 去除对该文件描述符的select事件监听

    PrintfdArray(); // Debug
}

// readfds集合里面包含的就是处于 “读事件就绪” 的sock
void HandlerEvent(int listensockfd, fd_set &readfds)
{
    auto It = fdArray.begin();
    for (; It != fdArray.end(); ++It)
    {
        if (*It == DEL)
            continue;
        // 按连接事件就绪类进行处理 -- 获取服务连接fd（IO类fd）并且放到select进行事件就绪监听
        if (It == fdArray.begin() && *It == listensockfd)
        {
            // 判断listensockfd在集合的“读事件是否就绪”（是否有连接已经完成三次握手被放到全连接队列，等待拿(accept)上去）
            if (FD_ISSET(listensockfd, &readfds))
            {
                cout << "等待成功!!! --> (时间戳: " << (unsigned long)time(nullptr) << ")" << endl;
                string ClientIp;
                uint16_t ClientPort;

                int serverfd = Sock::Accept(listensockfd, &ClientIp, &ClientPort); // 不会阻塞，连接事件已经就绪
                EstablishMap(serverfd, ClientIp, ClientPort);                      // 建立ip和port映射关系，保存用户网络数据
                // if (serverfd < 0) return;
                // cout << "获取新连接成功 -> " << ClientIp << ":" << ClientPort << " | fd: " << serverfd << endl;

                // read/write不知道底层的缓冲区是否有数据读取和缓冲区是否满了不能写入数据（“等”的过程）
                // 这里也要把serverfd交给select进行事件就绪监听 -- 这里是监听“IO事件” -- 前面监听的是“连接事件”
                auto It2 = fdArray.begin();
                while (It2 != fdArray.end())
                {
                    if (*It2 == DEL)
                        break;
                    ++It2;
                }
                if (It2 == fdArray.end())
                {
                    cerr << "Server已经到最大的上限, 无法保持更多的连接了!!!" << endl;
                    close(serverfd);
                }
                else
                {
                    int Index = It2 - fdArray.begin();
                    fdArray[Index] = serverfd; // 完成将serverfd放到select中进行IO事件监听

                    PrintfdArray();   // Debug
                }
            }
        }
        else
        {
            // 处理IO类事件就绪 -- read/recv...
            if (FD_ISSET(*It, &readfds))
            {
                char buffer[BUFFER_SIZE];
                string UserData;
                ssize_t rcv = recv(*It, buffer, BUFFER_SIZE - 1, 0);
                if (rcv > 0)
                {
                    buffer[rcv] = '\0';
                    for (auto KV : UserNetworkData)
                    {
                        if (KV.first == *It)
                        {
                            UserData = KV.second;
                            cout << "Client[" << UserData << "]" << "# " << buffer << endl;
                            break;
                        }
                    }
                }
                else if (recv == 0)
                {
                    recvError_Or_Close(CLIENT_CLOSE, *It, UserData);
                }
                else
                {
                    recvError_Or_Close(RECV_ERROR, *It, UserData);
                }
            }
        }
    }
}
